#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author       : Abmer
# @Date         : 2019-07-27 10:23:47
# @Link         : https://gitee.com/edrik
# @Modified by  : Amber
# @Modified at  : 2019-08-21 15:03:10
# @Version      : v.0.1

import ctypes
import ctypes.wintypes

from ctypes import (CFUNCTYPE, WINFUNCTYPE, Structure, POINTER)
# from ctypes.wintypes import *
from ..oskit import *


def utf16tostr(addr, size=-1):
    """Read UTF-16 string from memory and encode as python string."""
    if addr is None:
        return None

    cb = size if size > 0 else 32
    bstr = ctypes.string_at(addr, cb)
    if size >= 0:
        return bstr.decode('utf-16le')

    # lookup zero char
    chunks = []
    while True:
        found = cb
        for i in range(0, cb, 2):
            c = bstr[i]
            if c == 0x00:
                found = i
                break
            pass
        assert found % 2 == 0, "truncated string with len " + str(found)
        chunks.append(bstr[0:found].decode('utf-16le'))
        if found != cb:
            break
        addr = addr + cb
        bstr = ctypes.string_at(addr, cb)
        continue
    return "".join(chunks)


class c_utf16_p(ctypes.c_char_p):
    """A ctypes wrapper for UTF-16 string pointer."""

    # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
    def __init__(self, value=None):
        super(c_utf16_p, self).__init__()
        if value is not None:
            self.value = value

    @property
    def value(self, c_void_p=ctypes.c_void_p):
        addr = c_void_p.from_buffer(self).value
        return utf16tostr(addr)

    @value.setter
    def value(self, value, c_char_p=ctypes.c_char_p):
        value = value.encode('utf-16le') + b'\x00'
        c_char_p.value.__set__(self, value)

    @classmethod
    def from_param(cls, obj):
        if isinstance(obj, str):
            obj = obj.encode('utf-16le') + b'\x00'
        return super(c_utf16_p, cls).from_param(obj)

    @classmethod
    def _check_retval_(cls, result):
        return result.value

    pass


class UTF16LEField(object):
    """Structure member wrapper for UTF-16 string pointers."""

    # Taken from https://stackoverflow.com/a/35507014/736762, thanks to @eryksun.
    def __init__(self, name):
        self.name = name

    def __get__(self, obj, cls, c_void_p=ctypes.c_void_p, addressof=ctypes.addressof):
        field_addr = addressof(obj) + getattr(cls, self.name).offset
        addr = c_void_p.from_address(field_addr).value
        return utf16tostr(addr)

    def __set__(self, obj, value):
        value = value.encode('utf-16le') + b'\x00'
        setattr(obj, self.name, value)

    pass


# system
OS, ARCH = sys.platform, platform.architecture()[0]
OS_WIN = OS == 'win32'
OS_LNX = OS == 'linux'
OS_OSX = OS == 'darwin'

ARCH_32 = ARCH == '32bit'
ARCH_64 = ARCH == '64bit'

FN = CFUNCTYPE
CALLBACK = CFUNCTYPE

if OS_WIN:
    FN = WINFUNCTYPE
    CALLBACK = WINFUNCTYPE
else:
    raise SystemError('Unsupport system %s' % OS)

# ###################################################################################
# form %PYTHON_HOME%/lib/ctypes/wintypes.py
# ###################################################################################

BYTE = ctypes.c_byte
WORD = ctypes.c_ushort
DWORD = ctypes.c_ulong

# UCHAR = ctypes.c_uchar
CHAR = ctypes.c_char
WCHAR = ctypes.c_wchar
UINT = ctypes.c_uint
INT = ctypes.c_int

DOUBLE = ctypes.c_double
FLOAT = ctypes.c_float

BOOLEAN = BYTE
BOOL = ctypes.c_long


class VARIANT_BOOL(ctypes._SimpleCData):
    _type_ = "v"

    def __repr__(self):
        return "%s(%r)" % (self.__class__.__name__, self.value)


ULONG = ctypes.c_ulong
LONG = ctypes.c_long

USHORT = ctypes.c_ushort
SHORT = ctypes.c_short

# in the windows header files, these are structures.
_LARGE_INTEGER = LARGE_INTEGER = ctypes.c_longlong
_ULARGE_INTEGER = ULARGE_INTEGER = ctypes.c_ulonglong

LPCOLESTR = LPOLESTR = OLESTR = ctypes.c_wchar_p
LPCWSTR = LPWSTR = ctypes.c_wchar_p
LPCSTR = LPSTR = ctypes.c_char_p
LPCVOID = LPVOID = ctypes.c_void_p

# WPARAM is defined as UINT_PTR (unsigned type)
# LPARAM is defined as LONG_PTR (signed type)
if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
    WPARAM = ctypes.c_ulong
    LPARAM = ctypes.c_long
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
    WPARAM = ctypes.c_ulonglong
    LPARAM = ctypes.c_longlong

ATOM = WORD
LANGID = WORD

COLORREF = DWORD
LGRPID = DWORD
LCTYPE = DWORD

LCID = DWORD

################################################################
# HANDLE types
HANDLE = ctypes.c_void_p  # in the header files: void *

HACCEL = HANDLE
HBITMAP = HANDLE
HBRUSH = HANDLE
HCOLORSPACE = HANDLE
HDC = HANDLE
HDESK = HANDLE
HDWP = HANDLE
HENHMETAFILE = HANDLE
HFONT = HANDLE
HGDIOBJ = HANDLE
HGLOBAL = HANDLE
HHOOK = HANDLE
HICON = HANDLE
HINSTANCE = HANDLE
HKEY = HANDLE
HKL = HANDLE
HLOCAL = HANDLE
HMENU = HANDLE
HMETAFILE = HANDLE
HMODULE = HANDLE
HMONITOR = HANDLE
HPALETTE = HANDLE
HPEN = HANDLE
HRGN = HANDLE
HRSRC = HANDLE
HSTR = HANDLE
HTASK = HANDLE
HWINSTA = HANDLE
HWND = HANDLE
SC_HANDLE = HANDLE
SERVICE_STATUS_HANDLE = HANDLE

################################################################
# Some important structure definitions


class RECT(ctypes.Structure):
    _fields_ = [("left", LONG), ("top", LONG), ("right", LONG), ("bottom", LONG)]


tagRECT = _RECTL = RECTL = RECT


class _SMALL_RECT(ctypes.Structure):
    _fields_ = [('Left', SHORT), ('Top', SHORT), ('Right', SHORT), ('Bottom', SHORT)]


SMALL_RECT = _SMALL_RECT


class _COORD(ctypes.Structure):
    _fields_ = [('X', SHORT), ('Y', SHORT)]


class POINT(ctypes.Structure):
    _fields_ = [("x", LONG), ("y", LONG)]


tagPOINT = _POINTL = POINTL = POINT
PPOINT = LPPOINT = POINTER(POINT)


class SIZE(ctypes.Structure):
    _fields_ = [("cx", LONG), ("cy", LONG)]


tagSIZE = SIZE_T = SIZEL = SIZE
PSIZE = LPSIZE = POINTER(SIZE)


class MSG(ctypes.Structure):
    _fields_ = [("hWnd", HWND), ("message", UINT), ("wParam", WPARAM), ("lParam", LPARAM), ("time", DWORD),
                ("pt", POINT)]


tagMSG = MSG
PMSG = LPMSG = POINTER(MSG)


def RGB(red, green, blue):
    return red + (green << 8) + (blue << 16)


class FILETIME(ctypes.Structure):
    _fields_ = [("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD)]


_FILETIME = FILETIME

MAX_PATH = 260


class WIN32_FIND_DATAA(ctypes.Structure):
    _fields_ = [("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), ("ftLastAccessTime", FILETIME),
                ("ftLastWriteTime", FILETIME), ("nFileSizeHigh", DWORD), ("nFileSizeLow", DWORD),
                ("dwReserved0", DWORD), ("dwReserved1", DWORD), ("cFileName", CHAR * MAX_PATH),
                ("cAlternateFileName", CHAR * 14)]


class WIN32_FIND_DATAW(ctypes.Structure):
    _fields_ = [("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), ("ftLastAccessTime", FILETIME),
                ("ftLastWriteTime", FILETIME), ("nFileSizeHigh", DWORD), ("nFileSizeLow", DWORD),
                ("dwReserved0", DWORD), ("dwReserved1", DWORD), ("cFileName", WCHAR * MAX_PATH),
                ("cAlternateFileName", WCHAR * 14)]


################################################################
# Pointer types

LPBOOL = PBOOL = ctypes.POINTER(BOOL)
PBOOLEAN = ctypes.POINTER(BOOLEAN)
LPBYTE = PBYTE = ctypes.POINTER(BYTE)
PCHAR = ctypes.POINTER(CHAR)
LPCOLORREF = ctypes.POINTER(COLORREF)
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
LPFILETIME = PFILETIME = ctypes.POINTER(FILETIME)
PFLOAT = ctypes.POINTER(FLOAT)
LPHANDLE = PHANDLE = ctypes.POINTER(HANDLE)
PHKEY = ctypes.POINTER(HKEY)
LPHKL = ctypes.POINTER(HKL)
LPINT = PINT = ctypes.POINTER(INT)
PLARGE_INTEGER = ctypes.POINTER(LARGE_INTEGER)
PLCID = ctypes.POINTER(LCID)
LPLONG = PLONG = ctypes.POINTER(LONG)
LPMSG = PMSG = ctypes.POINTER(MSG)
LPPOINT = PPOINT = ctypes.POINTER(POINT)
PPOINTL = ctypes.POINTER(POINTL)
LPRECT = PRECT = ctypes.POINTER(RECT)
LPRECTL = PRECTL = ctypes.POINTER(RECTL)
LPSC_HANDLE = ctypes.POINTER(SC_HANDLE)
PSHORT = ctypes.POINTER(SHORT)
LPSIZE = PSIZE = ctypes.POINTER(SIZE)
LPSIZEL = PSIZEL = ctypes.POINTER(SIZEL)
PSMALL_RECT = ctypes.POINTER(SMALL_RECT)
LPUINT = PUINT = ctypes.POINTER(UINT)
PULARGE_INTEGER = ctypes.POINTER(ULARGE_INTEGER)
PULONG = ctypes.POINTER(ULONG)
PUSHORT = ctypes.POINTER(USHORT)
PWCHAR = ctypes.POINTER(WCHAR)
LPWIN32_FIND_DATAA = PWIN32_FIND_DATAA = ctypes.POINTER(WIN32_FIND_DATAA)
LPWIN32_FIND_DATAW = PWIN32_FIND_DATAW = ctypes.POINTER(WIN32_FIND_DATAW)
LPWORD = PWORD = ctypes.POINTER(WORD)

# ###################################################################################
# END
# ###################################################################################

# ###################################################################################
# self-define types
# ###################################################################################
PVOID = ctypes.c_void_p
PWCHAR = ctypes.c_wchar_p  # re-define
UTF8 = ctypes.c_char
PUTF8 = ctypes.c_char_p
VOID = None

FN_CALLBACK = PVOID
FN_JS_NATIVE = PVOID
HWND_FRAME = HWND
HWND_WEB_VIEW = HWND
JS_EXEC_STATE = HWND
# JS_VALUE = ctypes.c_longlong
JS_VALUE = PVOID
JS_TYPE = PVOID

P_JS_VALUE = POINTER(JS_VALUE)  # ?
WEB_VIEW = HWND
P_JS_DATA = PVOID


class wkeProxyType(ctypes.Structure):
    _fields_ = []


class wkeProxy(ctypes.Structure):
    _fields_ = [('type', wkeProxyType), ('hostname', PCHAR), ('port', UINT), ('username', PCHAR), ('password', PCHAR)]


class wkeWindowFeatures(Structure):
    _fields_ = [('x', INT), ('y', INT), ('width', INT), ('height', INT), ('menuBarVisible', BOOL),
                ('statusBarVisible', BOOL), ('toolBarVisible', BOOL), ('locationBarVisible', BOOL),
                ('scrollbarsVisible', BOOL), ('resizable', BOOL), ('fullscreen', BOOL)]


class wkeString(Structure):
    _fields_ = [('m_string', PWCHAR)]


jsGetPropertyCallback = FN(JS_VALUE, JS_EXEC_STATE, JS_VALUE, PCHAR)
jsSetPropertyCallback = FN(BOOL, JS_EXEC_STATE, JS_VALUE, PCHAR, JS_VALUE)

jsCallAsFunctionCallback = FN(JS_VALUE, JS_EXEC_STATE, JS_VALUE, P_JS_VALUE, INT)
jsFinalizeCallback = FN(VOID, P_JS_DATA)


class JsData(Structure):
    """docstring for jsData"""
    _fields_ = [('typeName', CHAR * 100), ('propertyGet', jsGetPropertyCallback),
                ('propertySet', jsSetPropertyCallback), ('finalize', jsFinalizeCallback),
                ('callAsFunction', jsCallAsFunctionCallback)]


P_JS_DATA_X = POINTER(JsData)

P_WKE_MEM_BUF = PVOID
P_JS_KEYS = PVOID
P_J_EXCEPT_INFO = PVOID
'''
#Window Notifications https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-create

WM_ACTIVATEAPP
WM_CANCELMODE
WM_CHILDACTIVATE
WM_CLOSE
WM_COMPACTING
WM_CREATE
WM_DESTROY
WM_DPICHANGED
WM_ENABLE
WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
WM_GETICON
WM_GETMINMAXINFO
WM_INPUTLANGCHANGE
WM_INPUTLANGCHANGEREQUEST
WM_MOVE
WM_MOVING
WM_NCACTIVATE
WM_NCCALCSIZE
WM_NCCREATE
WM_NCDESTROY
WM_NULL
WM_QUERYDRAGICON
WM_QUERYOPEN
WM_QUIT
WM_SHOWWINDOW
WM_SIZE
WM_SIZING
WM_STYLECHANGED
WM_STYLECHANGING
WM_THEMECHANGED
WM_USERCHANGED
WM_WINDOWPOSCHANGED
WM_WINDOWPOSCHANGING
'''